<

グラデーションのチャットバブルを作成する

従来のチャット アプリはチャット バブルにメッセージを表示します 単色の背景。最新のチャット アプリの表示 に基づいたグラデーションを使用したチャットバブル 画面上の泡の位置。 このレシピでは、以下を実装してチャット UI を最新化します。 チャットバブルのグラデーション背景。

次のアニメーションはアプリの動作を示しています。

Scrolling the gradient chat bubbles

課題を理解する

従来のチャット バブル ソリューションでは、おそらくDecoratedBoxまたは同様のウィジェットを使用して丸いものをペイントします 各チャット メッセージの後ろにある四角形。そのアプローチは、 単色やグラデーションにも最適です。 すべてのチャットバブルで繰り返されます。しかしながら、現代では、 フルスクリーン、グラデーションバブルの背景には必要があります 別のアプローチ。全画面グラデーション、 画面を上下にスクロールするバブルと組み合わせて、 絵を描くためのアプローチが必要です レイアウト情報に基づいて決定します。

各バブルの勾配には、次の知識が必要です。 画面上のバブルの位置。この意味は ペイント動作にはレイアウト情報へのアクセスが必要です。 このような描画動作は、一般的なウィジェットでは不可能です なぜならウィジェットは次のようなものだからですContainerDecoratedBoxレイアウトが行われる前に背景色を決定します。 その後ではありません。この場合、カスタムペイントが必要となるため、 動作しますが、カスタム レイアウト動作は必要ありません またはカスタム ヒット テスト動作、CustomPainterは 仕事をやり遂げるには素晴らしい選択です。

元の背景ウィジェットを置き換える

描画を担当するウィジェットを置き換えます。 という新しいステートレス ウィジェットが背景に追加されましたBubbleBackground。を含めるcolors財産を あるべき全画面のグラデーションを表します。 バブルに適用されます。

BubbleBackground(
  // The colors of the gradient, which are different
  // depending on which user sent this message.
  colors: message.isMine
      ? const [Color(0xFF6C7689), Color(0xFF3A364B)]
      : const [Color(0xFF19B7FF), Color(0xFF491CCB)],
  // The content within the bubble.
  child: DefaultTextStyle.merge(
    style: const TextStyle(
      fontSize: 18.0,
      color: Colors.white,
    ),
    child: Padding(
      padding: const EdgeInsets.all(12),
      child: Text(message.text),
    ),
  ),
);

カスタム ペインタを作成する

次に、次の実装を紹介します。BubbleBackgroundステートレスなウィジェットとして。現時点では、build()を返すメソッドCustomPaintとともにCustomPainter呼ばれたBubblePainterBubblePainter塗装に使用されます バブルのグラデーション。

@immutable
class BubbleBackground extends StatelessWidget {
  const BubbleBackground({
    super.key,
    required this.colors,
    this.child,
  });

  final List<Color> colors;
  final Widget? child;

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: BubblePainter(
        colors: colors,
      ),
      child: child,
    );
  }
}

class BubblePainter extends CustomPainter {
  BubblePainter({
    required List<Color> colors,
  }) : _colors = colors;

  final List<Color> _colors;

  @override
  void paint(Canvas canvas, Size size) {
    // TODO:
  }

  @override
  bool shouldRepaint(BubblePainter oldDelegate) {
    // TODO:
    return false;
  }
}

スクロール情報へのアクセスを提供する

CustomPainter必要な情報が必要です 気泡が内部のどこにあるかを判断するListViewの境界、 としても知られていますViewport。場所を決定するには次のことが必要です 先祖への言及ScrollableStateそして、への参照BubbleBackgroundBuildContext。それぞれをCustomPainter

BubblePainter(
  colors: colors,
  bubbleContext: context,
  scrollable: ScrollableState(),
),
このコードの抜粋では、追加の閉じ括弧が追加されています 抜粋ではカスタム ペインタに必要なペイント メソッドが省略されているため、最後に追加されています。
class BubblePainter extends CustomPainter {
  BubblePainter({
    required ScrollableState scrollable,
    required BuildContext bubbleContext,
    required List<Color> colors,
  })  : _scrollable = scrollable,
        _bubbleContext = bubbleContext,
        _colors = colors;

  final ScrollableState _scrollable;
  final BuildContext _bubbleContext;
  final List<Color> _colors;

  @override
  bool shouldRepaint(BubblePainter oldDelegate) {
    return oldDelegate._scrollable != _scrollable ||
        oldDelegate._bubbleContext != _bubbleContext ||
        oldDelegate._colors != _colors;
  }
}

フルスクリーンのバブルグラデーションをペイントする

CustomPainter希望のグラデーションカラーになりました。 含まれているものへの参照ScrollableState、 そしてこのバブルへの言及BuildContext。 これがすべての情報です。CustomPainterする必要があります フルスクリーンのバブルグラデーションをペイントします。 を実装します。paint()位置を計算する方法 バブルの、指定された色でシェーダーを構成し、 次に、マトリックス変換を使用してシェーダーをオフセットします。 内のバブルの位置に基づいて、Scrollable

class BubblePainter extends CustomPainter {
  BubblePainter({
    required ScrollableState scrollable,
    required BuildContext bubbleContext,
    required List<Color> colors,
  })  : _scrollable = scrollable,
        _bubbleContext = bubbleContext,
        _colors = colors;

  final ScrollableState _scrollable;
  final BuildContext _bubbleContext;
  final List<Color> _colors;

  @override
  bool shouldRepaint(BubblePainter oldDelegate) {
    return oldDelegate._scrollable != _scrollable ||
        oldDelegate._bubbleContext != _bubbleContext ||
        oldDelegate._colors != _colors;
  }

  @override
  void paint(Canvas canvas, Size size) {
    final scrollableBox = _scrollable.context.findRenderObject() as RenderBox;
    final scrollableRect = Offset.zero & scrollableBox.size;
    final bubbleBox = _bubbleContext.findRenderObject() as RenderBox;

    final origin =
        bubbleBox.localToGlobal(Offset.zero, ancestor: scrollableBox);
    final paint = Paint()
      ..shader = ui.Gradient.linear(
        scrollableRect.topCenter,
        scrollableRect.bottomCenter,
        _colors,
        [0.0, 1.0],
        TileMode.clamp,
        Matrix4.translationValues(-origin.dx, -origin.dy, 0.0).storage,
      );
    canvas.drawRect(Offset.zero & size, paint);
  }
}

おめでとう!これで、最新のチャット バブル UI が完成しました。

要約

に基づいてペイントする場合の基本的な課題 スクロール位置、または一般的な画面位置、 ペイント動作は、 レイアウト段階が完了しました。CustomPaintユニークです カスタムペイントを実行できるウィジェット レイアウトフェーズが完了した後の動作。 レイアウトフェーズの後にペイントビヘイビアーを実行すると、 レイアウトに基づいてペイントを決定できます の位置などの情報CustomPaint内のウィジェットScrollableまたは画面内で。